﻿using System;
using System.Collections.Generic;
using System.Security.AccessControl;
using DevicesInt;
using monitor_2_DBwork;
using Timer = System.Threading.Timer;
using System.Threading;
using System.IO;
using System.Text;

namespace ServerLib.Entities
{
    public static class SaveLogs
    {
        private static bool _initSuccess;
        private static readonly Dictionary<string, Device> Devices = new Dictionary<string, Device>();  //Список всех устройств
        private static Dictionary<string, string> _threads; //Хранит результат выполнения задач по сохранению журналов
        private static Thread _saveLogsThread;              //Главный поток сохранения логов
        private static int _saveLogsDelay;                  //Задержка между опросами
        //private static int _threadsCount;
        private static readonly Timer SaveLogsDelayTimer = new Timer(TimerCallBack);
        private static readonly string DelayOptionName = Server.Rm.GetString("SaveDeviceLogsDelay_option");
        private static string _dir;
        static readonly ThreadOptions _threadOptions = new ThreadOptions();

        private delegate KeyValuePair<string, string> SaveLogsDelegate(DllUnmanagedWork dll, string ip, string logFilePath);

        private static class SharedRes
        {
            public static Mutex Mtx;
        }

        private class ThreadOptions                 //Информация об общем потоке сохранения журналов
        {
            public ThreadOptions()
            {
                ThreadsCount = 0;
                IsStopRequested = false;
                IsStopped = true;
                //IsCompleted = false;
            }
            public int ThreadsCount { get; set; }       //Количество запущенных потоков
            public bool IsStopRequested { get; set; }   //Запрос на остановку
            public bool IsStopped { get; set; }
            //public bool IsCompleted { get; set; }       //Завершен ли цикл сохранения
        }

        //Запуск основного потока
        public static void Start()
        {
            var d = new ThreadStart(StartThread);
            lock (_threadOptions)
            {
                _threadOptions.IsStopRequested = false;
                _threadOptions.IsStopped = false;
            }
            _saveLogsThread = new Thread(d);
            _saveLogsThread.Start();
        }

        //Остановка основного потока
        public static void Stop()
        {
            lock (_threadOptions)
            {
                if (!_initSuccess)
                    return;
                if (_threadOptions.IsStopped)
                    return;
                _threadOptions.IsStopRequested = true;
            }
            while (true)
            {
                
                lock (_threadOptions)
                {
                    if (_threadOptions.IsStopped)
                    {
                        SharedRes.Mtx.Close();
                        return;
                    }
                }
                Thread.Sleep(200);
            }
        }

        //Заставляет снова получить все параметры (список устройств, значение параметра задержки между опросами)
        public static void Reset()
        {
            _initSuccess = false;
        }

        private class Device
        {
            public readonly DllUnmanagedWork LibWork; 
            public readonly List<AdditionalInfo> IpList;
            

            //public Device(string libName, string devName)
            public Device(string devName)
            {
                _initSuccess = false;
                //LibWork = new DllUnmanagedWork(libName, devName);
                LibWork = Server.DeviceLibraries.GetByDeviceName(devName);
                IpList = new List<AdditionalInfo>();
            }
            
            public class AdditionalInfo
            {
                public AdditionalInfo(string ip, string path)
                {
                    Ip = ip;
                    //RTPSName = rtps;
                    LogFilePath = path;
                }
                public string Ip { get; private set; }
                //private string RTPSName { get; set; }
                public string LogFilePath { get; private set; }
            }
            
        }

        private static bool Initialize()
        {
            List<string[]> deviceLibs = DBwork.GetDeviceLibsList();
            // 0 - ip
            // 1 - lib name
            // 2 - dev name
            // 3 - rtps name
            if (deviceLibs[0][0] == "err:")
            {
                Server.WriteToLog(deviceLibs[0][1], ServerCategories.Warning);
                return _initSuccess;
            }

            #region dir

            _dir = CurDir.Get;
            if(!Directory.Exists(_dir + "\\Resources"))
                try
                {
                    Directory.CreateDirectory(_dir + "\\Resources");
                }
                catch (Exception ex)
                {
                    Server.WriteToLog(ex.Message, ServerCategories.Warning);
                    return _initSuccess;
                }
            if (!Directory.Exists(_dir + "\\Resources\\logs"))
                try
                {
                    Directory.CreateDirectory(_dir + "\\Resources\\logs");
                }
                catch (Exception ex)
                {
                    Server.WriteToLog(ex.Message, ServerCategories.Warning);
                    return _initSuccess;
                }
            _dir += "\\Resources\\logs";

            #endregion
            Devices.Clear();
            foreach (var device in deviceLibs)
            {

                if (!Devices.ContainsKey(device[2]))
                {
                    //Devices.Add(device[2], new Device(device[1], device[2]));
                    Devices.Add(device[2], new Device(device[2]));
                }

                var sb = new StringBuilder();
                sb.Append(_dir);
                sb.Append("\\" + device[3] + "_");
                var date = DateTime.Now;
                //sb.Append(DateTime.Now.ToString(CultureInfo.InvariantCulture) + ";");
                sb.Append(date.Month + ".");
                sb.Append(date.Day + " ");
                sb.Append(date.Hour + ".");
                sb.Append(date.Minute + "_");
                sb.Append(device[2] + ".csv");

                var addInfo = new Device.AdditionalInfo(device[0], sb.ToString());
                Devices[device[2]].IpList.Add(addInfo);
            }
            if (!GetDelay())
                return _initSuccess;

            bool createdNewMutex;
            SharedRes.Mtx = new Mutex(false, "SaveLogsMutex", out createdNewMutex);
            if (!createdNewMutex)
                return _initSuccess;

            return _initSuccess = true;
        }

        //Получает значение параметра задержки из БД
        private static bool GetDelay()
        {
            string optionsStr = DBwork.GetOptionsList();
            if (optionsStr.Contains("err:"))
            {
                Server.WriteToLog(optionsStr.Substring(4), ServerCategories.Warning);
                return false;
            }
            var options = optionsStr.Split(';');
            foreach (string option in options)
            {
                var split = option.Split(':');
                if (split[0] == DelayOptionName)
                {
                    int ret;
                    if (int.TryParse(split[1], out ret))
                    {
                        _saveLogsDelay = ret * 1000;//получаем секунды
                        return true;  
                    }
                    Server.WriteToLog(Server.Rm.GetString("ValueDefinitionError") + ": " + Server.Rm.GetString("SaveDeviceLogsDelay"), ServerCategories.Warning);
                    return false;
                }
            }
            Server.WriteToLog(Server.Rm.GetString("ValueSearchError") + ": " + Server.Rm.GetString("SaveDeviceLogsDelay"), ServerCategories.Warning);
            return false;
        }

        //Функция главного потока сохранения логов
        private static void StartThread()
        {
            //Server.WriteToLog("Сохранение логов запущено", false);
            if(!_initSuccess)
                if (!Initialize())
                    return;
            //_threadsCount = 0;
            lock (_threadOptions)
            {
                _threadOptions.ThreadsCount = 0;
                if (_threadOptions.IsStopRequested)
                {
                    _threadOptions.IsStopped = true;
                    return;
                }
            }
            _threads = new Dictionary<string, string>();
            //_threadOptions.IsCompleted = false;
            SharedRes.Mtx.WaitOne();
            foreach (var device in Devices)
            {
                foreach (Device.AdditionalInfo ipList in device.Value.IpList)
                {
                    //Дла каждого устройства запускает новый поток
                    var saveLogsDel = new SaveLogsDelegate(GetLog);
                    
                    saveLogsDel.BeginInvoke(device.Value.LibWork, ipList.Ip, ipList.LogFilePath, GetLogFileCallBack, saveLogsDel);
                    _threads.Add(ipList.Ip, "");

                    //_threadsCount++;
                    lock (_threadOptions)
                    {
                        _threadOptions.ThreadsCount++;
                    }
                }
            }
            SharedRes.Mtx.ReleaseMutex();
        }

        //Получение файла журнала
        private static KeyValuePair<string, string> GetLog(DllUnmanagedWork dll, string ip, string path)
        {
            //Получение файлов с логами
            var check = dll.GetLog(ip, path);
            if(check.Contains("err:"))
                return new KeyValuePair<string, string>(ip, check.Substring(4));
            //Запись данных из файлов в БД
            //check = DBwork.UpdateBD_FromCSV(path);
            //if (check != "ok")
            //{
            //    return new KeyValuePair<string, string>(ip, check.Substring(4));
            //}
            //try
            //{
            //    File.Delete(path);
            //}
            //catch (Exception ex)
            //{
            //    return new KeyValuePair<string, string>(ip, ex.Message);
            //}
            return new KeyValuePair<string, string>(ip, check);
        }

        //Срабатывает после получения файла журнала
        private static void GetLogFileCallBack(IAsyncResult iar)
        {
            //_threadsCount--;
            var b = (SaveLogsDelegate) iar.AsyncState;
            var res = b.EndInvoke(iar);
            if (res.Value == "ok" || res.Value == Errors.NOT_IMPLEMENTED)
            {
                SharedRes.Mtx.WaitOne();
                _threads.Remove(res.Key);
                SharedRes.Mtx.ReleaseMutex();
            }
            else
            {
                SharedRes.Mtx.WaitOne();
                _threads[res.Key] = res.Value;
                SharedRes.Mtx.ReleaseMutex();
            }
            bool checkT;
            lock (_threadOptions)
            {
                _threadOptions.ThreadsCount--;
                checkT = _threadOptions.ThreadsCount == 0;
            }
            if (checkT)
            {
                //Запись ошибок в БД
                SharedRes.Mtx.WaitOne();
                foreach (var thread in _threads)
                {   //thread.Value приносит ошибку с индексом с т-випсов!!!
                    Server.WriteToLog(thread.Value + ": " + thread.Key, ServerCategories.Warning);
                    
                }
                SharedRes.Mtx.ReleaseMutex();

                //Обработка файлов с журналами, которые не были раньше добавлены в базу
                var files = Directory.GetFiles(_dir);
                if (files.Length > 0)
                {
                    foreach (string file in files)
                    {
                        var check = DBwork.UpdateBD_FromCSV(file);
                        if (check != "ok")
                        {
                            Server.WriteToLog(check + ": " + file, ServerCategories.Warning);
                        }
                        else
                        {
                            try
                            {
                                File.Delete(file);
                            }
                            catch (Exception ex)
                            {
                                Server.WriteToLog(ex.Message + ": " + file, ServerCategories.Warning);
                            }
                        }
                    }
                }

                //Подтверждение завершения операции обновления журнала
                //Server.WriteToLog(Server.Rm.GetString("LogSavingComplete"), ServerCategories.Info);
                Server.OnSaveLogsComplete();
                lock (_threadOptions)
                {
                    checkT = _threadOptions.IsStopRequested;
                }
                if (checkT)
                {
                    SaveLogsDelayTimer.Change(Timeout.Infinite, 0);
                    lock (_threadOptions)
                    {
                        _threadOptions.IsStopped = true;
                    }
                    //Server.WriteToLog("Сохранение логов остановлено", false);
                    SaveLogsDelayTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    return;
                }

                SaveLogsDelayTimer.Change(_saveLogsDelay, 0);
            }
        }

        //Срабатывает по таймеру
        static void TimerCallBack(object obj)
        {
            SaveLogsDelayTimer.Change(Timeout.Infinite, 0);
            StartThread();
        }

    }
}
